﻿using System.Collections;
using Python.Runtime;

namespace DocPython;

public static class PythonRun
{
    public static string GetPythonPath()
    {
        var dir = new DirectoryInfo(
            $@"{Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)}\Programs\Python");
        var folder = dir.GetDirectories("Python*").FirstOrDefault();
        if (folder == null) return "";
        return Path.Combine(folder.FullName, $"{folder.Name.ToLower()}.dll");
    }

    public static void Init()
    {
        var dll = GetPythonPath();
        Runtime.PythonDLL = dll;
        PythonEngine.Initialize();
    }
    
    public static PyObject PythonFileRun(string filePath, string methodName, params object[] arg)
    {
        Py.GIL();
        using var scope = Py.CreateScope();
        var code = File.ReadAllText(filePath);
        var scriptCompiled = PythonEngine.Compile(code, filePath);
        scope.Execute(scriptCompiled);
        var pyArg = arg.Select(ObjToPy).ToArray();
        return scope.InvokeMethod(methodName, pyArg);
    }
    
    public static PyObject PythonFileRunSharp(string filePath, string methodName, params object[] arg)
    {
        using var a = Py.GIL();
        using var scope = Py.CreateScope();
        var code = File.ReadAllText(filePath);
        var scriptCompiled = PythonEngine.Compile(code, filePath);
        scope.Execute(scriptCompiled);
        var pyArg = arg.Select(x => x.ToPython()).ToArray();
        return scope.InvokeMethod(methodName, pyArg);
    }

    public static PyObject ObjToPy(object o)
    {
        switch (o)
        {
            case int i :
                return new PyInt(i);
            case IList list:
            {
                var pyList = new PyList();
                foreach (var a in list)
                    pyList.Append(ObjToPy(a));

                return pyList;
            }
            case double d:
                return new PyFloat(d);
            case float f:
                return new PyFloat(f);
            case DictionaryBase dictionaryBase:
            {
                var dict = new PyDict();
                foreach (DictionaryEntry entry in dictionaryBase)
                    dict[ObjToPy(entry.Key)] = ObjToPy(entry.Value!);
                
                return dict;
            }
            case string s:
                return new PyString(s);
            default:
                return o.ToPython();
        }
    }

    public static void Close()
    {
        try
        {
            PythonEngine.Shutdown();
        }
        catch
        {
            //
        }
    }
    
    public static void Test()
    {
        var dll = GetPythonPath();
        Runtime.PythonDLL = dll;
        PythonEngine.Initialize();
        using (Py.GIL())
        {
            dynamic np = Py.Import("numpy");
            Console.WriteLine(np.cos(np.pi * 2));

            dynamic sin = np.sin;
            Console.WriteLine(sin(5));

            double c = (double)(np.cos(5) + sin(5));
            Console.WriteLine(c);

            dynamic a = np.array(new List<float> { 1, 2, 3 });
            Console.WriteLine(a.dtype);

            dynamic b = np.array(new List<float> { 6, 5, 4 }, dtype: np.int32);
            Console.WriteLine(b.dtype);

            Console.WriteLine(a * b);
        }
    }
}